package com.ruoyi.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.ruoyi.domain.FileUploadTask;
import com.ruoyi.domain.MFile;
import com.ruoyi.dto.DocumentFile;
import com.ruoyi.dto.InitTaskParam;
import com.ruoyi.dto.TaskInfoDTO;
import com.ruoyi.dto.TaskRecordDTO;
import com.ruoyi.mapper.FileUploadTaskMapper;
import com.ruoyi.platform.constant.MinioConstant;
import com.ruoyi.platform.core.domain.entity.SysDictData;
import com.ruoyi.platform.service.impl.BaseServiceImpl;
import com.ruoyi.platform.system.service.ISysDictDataService;
import com.ruoyi.service.IFileUploadTaskService;
import com.ruoyi.service.IMFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 分片上传-分片任务记录Service业务层处理
 *
 * @author ruoyi
 * @date 2023-02-03
 */
@Service
@Slf4j
public class FileUploadTaskServiceImpl extends BaseServiceImpl<FileUploadTaskMapper, FileUploadTask> implements IFileUploadTaskService {

    @Autowired
    private FileUploadTaskMapper fileUploadTaskMapper;

    @Resource
    private AmazonS3 amazonS3;

    @Value("${minio.bucket-name:'megmeet-bucket'}")
    private String bucket;

    @Value("${minio.url}")
    private String url;

    @Value("${minio.end-point}")
    private String endPoint;

    @Autowired
    private ISysDictDataService dictDataService;


    /**
     * 查询分片上传-分片任务记录
     *
     * @param id 分片上传-分片任务记录主键
     * @return 分片上传-分片任务记录
     */
    @Override
    public FileUploadTask selectFileUploadTaskById(Long id) {
        return fileUploadTaskMapper.selectFileUploadTaskById(id);
    }

    /**
     * 查询分片上传-分片任务记录列表
     *
     * @param fileUploadTask 分片上传-分片任务记录
     * @return 分片上传-分片任务记录
     */
    @Override
    public List<FileUploadTask> selectFileUploadTaskList(FileUploadTask fileUploadTask) {
        return fileUploadTaskMapper.selectFileUploadTaskList(fileUploadTask);
    }

    /**
     * 新增分片上传-分片任务记录
     *
     * @param fileUploadTask 分片上传-分片任务记录
     * @return 结果
     */
    @Transactional
    @Override
    public int insertFileUploadTask(FileUploadTask fileUploadTask) {
        return fileUploadTaskMapper.insert(fileUploadTask);
    }

    /**
     * 修改分片上传-分片任务记录
     *
     * @param fileUploadTask 分片上传-分片任务记录
     * @return 结果
     */
    @Transactional
    @Override
    public int updateFileUploadTask(FileUploadTask fileUploadTask) {
        return fileUploadTaskMapper.updateById(fileUploadTask);
    }

    /**
     * 批量删除分片上传-分片任务记录
     *
     * @param ids 需要删除的分片上传-分片任务记录主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteFileUploadTaskByIds(Long[] ids) {
        return fileUploadTaskMapper.deleteFileUploadTaskByIds(ids);
    }

    /**
     * 删除分片上传-分片任务记录信息
     *
     * @param id 分片上传-分片任务记录主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteFileUploadTaskById(Long id) {
        return fileUploadTaskMapper.deleteFileUploadTaskById(id);
    }

    /**
     * 获取上传进度
     * @param identifier
     * @return
     */
    @Override
    public TaskInfoDTO getTaskInfo(String identifier) {
        FileUploadTask task = getByIdentifier(identifier);
        if (task == null) {
            return null;
        }
        TaskInfoDTO result = new TaskInfoDTO().setFinished(true).setTaskRecord(TaskRecordDTO.convertFromEntity(task)).setPath(getPath(task.getBucketName(), task.getObjectKey()));
        //将该文件信息存放到m_file数据库
        String fileUrl=url.concat("/"+task.getBucketName()+"/").concat(task.getObjectKey());
        log.info("文件路径: " + fileUrl);
        MFile mFile=new MFile();
        mFile.setFileName(task.getFileName());
        mFile.setOriginName(task.getFileName());
        mFile.setFileUrl(fileUrl);
        mFile.insert();
        DocumentFile documentFile = getDocFile(mFile);
        result.setDocumentFile(documentFile);
        boolean doesObjectExist = amazonS3.doesObjectExist(task.getBucketName(), task.getObjectKey());
        if (!doesObjectExist) {
            // 未上传完，返回已上传的分片
            ListPartsRequest listPartsRequest = new ListPartsRequest(task.getBucketName(), task.getObjectKey(), task.getUploadId());
            PartListing partListing = amazonS3.listParts(listPartsRequest);
            result.setFinished(false).getTaskRecord().setExitPartList(partListing.getParts());
            result.setDocumentFile(new DocumentFile());
        }
        return result;
    }

    /**
     * 创建一个上传任务
     * @param param
     * @return
     */
    @Override
    public TaskInfoDTO initTask(InitTaskParam param) {
        Date currentDate = new Date();
        String bucketName = bucket;
        String fileName = param.getFileName();
        String suffix = fileName.substring(fileName.lastIndexOf(".")+1, fileName.length());
        String key = StrUtil.format("{}/{}.{}", DateUtil.format(currentDate, "YYYY-MM-dd"), IdUtil.randomUUID(), suffix);
        String contentType = MediaTypeFactory.getMediaType(key).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(contentType);
        InitiateMultipartUploadResult initiateMultipartUploadResult = amazonS3
                .initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key).withObjectMetadata(objectMetadata));
        String uploadId = initiateMultipartUploadResult.getUploadId();

        FileUploadTask task = new FileUploadTask();
        int chunkNum = (int) Math.ceil(param.getTotalSize() * 1.0 / param.getChunkSize());
        task.setBucketName(bucketName);
        task.setChunkNum(chunkNum);
        task.setChunkSize(param.getChunkSize());
        task.setTotalSize(param.getTotalSize());
        task.setFileIdentifier(param.getIdentifier());
        task.setFileName(fileName);
        task.setObjectKey(key);
        task.setUploadId(uploadId);
        baseMapper.insert(task);
        return new TaskInfoDTO().setFinished(false).setTaskRecord(TaskRecordDTO.convertFromEntity(task)).setPath(getPath(bucketName, key));
    }

    @Override
    public FileUploadTask getByIdentifier(String identifier) {
        return baseMapper.selectOne(new LambdaQueryWrapper<FileUploadTask>().eq(FileUploadTask::getFileIdentifier,identifier));
    }

    /**
     * 生成预签名上传url
     * @param bucket 桶名
     * @param objectKey 对象的key
     * @param params 额外的参数
     * @return
     */
    @Override
    public String genPreSignUploadUrl(String bucket, String objectKey, Map<String, String> params) {
        Date currentDate = new Date();
        Date expireDate = DateUtil.offsetMillisecond(currentDate, MinioConstant.PRE_SIGN_URL_EXPIRE.intValue());
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, objectKey)
                .withExpiration(expireDate).withMethod(HttpMethod.PUT);
        if (params != null) {
            params.forEach((key, val) -> request.addRequestParameter(key, val));
        }
        URL preSignedUrl = amazonS3.generatePresignedUrl(request);
        return preSignedUrl.toString();
    }

    /**
     * 合并分片
     * @param identifier
     */
    @Override
    public DocumentFile merge(String identifier) {
        FileUploadTask task = getByIdentifier(identifier);
        if (task == null) {
            throw new RuntimeException("分片任务不存");
        }
        ListPartsRequest listPartsRequest = new ListPartsRequest(task.getBucketName(), task.getObjectKey(), task.getUploadId());
        PartListing partListing = amazonS3.listParts(listPartsRequest);
        List<PartSummary> parts = partListing.getParts();
        if (!task.getChunkNum().equals(parts.size())) {
            // 已上传分块数量与记录中的数量不对应，不能合并分块
            throw new RuntimeException("分片缺失，请重新上传");
        }
        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest()
                .withUploadId(task.getUploadId())
                .withKey(task.getObjectKey())
                .withBucketName(task.getBucketName())
                .withPartETags(parts.stream().map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag())).collect(Collectors.toList()));
        //将该文件信息存放到m_file数据库
        String fileUrl=url.concat("/"+task.getBucketName()+"/").concat(task.getObjectKey());
        log.info("文件路径: " + fileUrl);
        MFile mFile=new MFile();
        mFile.setFileName(task.getFileName());
        mFile.setOriginName(task.getFileName());
        mFile.setFileUrl(fileUrl);
        mFile.insert();
        DocumentFile documentFile=getDocFile(mFile);
        CompleteMultipartUploadResult result = amazonS3.completeMultipartUpload(completeMultipartUploadRequest);
        return documentFile;
    }

    /**
     * 上传每个分片
     * @param bucketName
     * @param objectKey
     * @param uploadId
     * @param partNumber
     * @param inputStream
     * @param isEnd
     * @param partSize
     * @return
     */
    @Override
    public UploadPartResult partUpload(String bucketName, String objectKey, String uploadId, Integer partNumber, InputStream inputStream, Boolean isEnd, Long partSize) {
        String contentType = MediaTypeFactory.getMediaType(objectKey).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(contentType);
        UploadPartRequest uploadPartRequest = new UploadPartRequest();
        uploadPartRequest.setBucketName(bucketName);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setPartNumber(partNumber);
        uploadPartRequest.setKey(objectKey);
        uploadPartRequest.setObjectMetadata(objectMetadata);
        uploadPartRequest.setInputStream(inputStream);
        uploadPartRequest.setLastPart(isEnd);
        uploadPartRequest.setPartSize(partSize);
        UploadPartResult uploadPartResult = amazonS3.uploadPart(uploadPartRequest);
        return uploadPartResult;
    }

    public String getPath(String bucket, String objectKey) {
        return StrUtil.format("{}/{}/{}", endPoint, bucket, objectKey);
    }

    public DocumentFile getDocFile(MFile mFile){
        String originName = mFile.getOriginName();
        DocumentFile documentFile=new DocumentFile();
        documentFile.setFileUrl(mFile.getFileUrl());
        documentFile.setFileId(mFile.getId());
        documentFile.setOriginFileName(originName);
        int index= originName.indexOf(".");
        String fileSuffix= originName.substring(index+1);
        documentFile.setSuffix(fileSuffix);
        List<SysDictData> sysDictData = dictDataService.selectDictDataByLabel(fileSuffix);
        String fileTypeName;
        if(sysDictData.size()!=0){
            fileTypeName = dictDataService.selectDictLabel("document_type", sysDictData.get(0).getDictValue());
            documentFile.setFileTypeName(fileTypeName);
            documentFile.setFileTypeId(sysDictData.get(0).getDictValue());
        }else{
            documentFile.setFileTypeName("其他文件");
            documentFile.setFileTypeId("7");
        }
        return documentFile;
    }

}
